【X68000(Z)アセンブラ講座 第017回 CG描画高速化】 2025/05/03   こんにちは!! 今回からは応用編という事で、 知っておくと便利なプログラミングテクニックを 僕が思い出せる限りお伝えする事にします。 今回は512×512ピクセルの画面の任意の座標に 256×256ピクセルの画像を高速に描画する方法の説明をします。 サンプルプログラムを動かす前に 256×256ピクセル8ビットカラー(256色)のBMP画像ファイルを用意して 'BM_TEST3.bmp'と言うファイル名で保存して下さい。 破線の内側を'BMP256_1.s'と言うファイル名で保存して下さい。 ------------------------------------------------------------------------------------------------ ******************************************************************************** * * アプリ名 : BMP256_1.x * * CG画像高速表示アプリ * * Ver1.00 * ******************************************************************************** include A:\XC\INCLUDE\DOSCALL.MAC * OSコール用マクロの読み込み include A:\XC\INCLUDE\IOCSCALL.MAC * IOCS(BIOS)コール用マクロの読み込み * 機種別パレット情報 * Win RGBQUAD { Blue[8bit], Green[8bit], Red[8bit], Alpha[8bit] } 32Bit Color * X68K Color { Green[5bit], Red[5bit], Blue[5bit], Alpha[1bit] } 16Bit Color * システム領域のアドレス cg_palette equ $E82000 * CG用パレットデータの先頭アドレス cg_buffer equ $C00000 * CG-VRAMの先頭アドレス cg_scroll_x equ $E80018 cg_scroll_y equ $E8001a sp_palette equ $E82200 vsync equ $E88001 * %00010000でV-Syncチェック .cpu 68000 * CPUのタイプ .data * '.data'以降のデータはデータセクションに配置される file00: dc.b 'BM_TEST3.bmp',0 * 読み込むBMPのファイル名 msg_return: dc.b 13,10,0 * 改行コード('13'+'10') & 終端コード('0') msg01: dc.b 'BMP File Name : ',0 msg02: dc.b 'File Open Error !',13,10,0 msg03: dc.b 'BMP形式のファイルではありません !',13,10,0 msg04: dc.b '8ビットカラー(256色)の画像ではありません !',13,10,0 msg05: dc.b 'CGのサイズが256×256ピクセルではありません !',13,10,0 .even * '.even'以降の偶数アドレスにデータを置く fh: dc.w 0 * ファイルハンドル(ファイル管理番号) viewW: dc.l 0 viewH: dc.l 0 bmp_header_start: * bmp_header_endまでBMPファイルのヘッダーデータ領域 BMFH: * BITMAPFILEHEADER ビットマップファイルヘッダー 14bytes bmph: dc.b 0, 0 * 'B'と'M'ならばBMP形式画像 dc.l 0 * BMPファイルのサイズ dc.w 0 * 予約領域1 dc.w 0 * 予約領域2 dc.l 0 * ファイルヘッダの先頭からビットマップの先頭までの長さ BMIH: * BITMAPINFOHEADER ビットマップインフォヘッダー 40bytes biSize: dc.l 0 * BITMAPINFOHEADER領域のバイト数 biWidth: dc.w 0, 0 * 横ピクセル数 biHeight: dc.w 0, 0 * 縦ピクセル数 biPlanes: dc.w 0 * 1 biBitCount: dc.w 0 * カラーモード(4/8/16/24/32) biCompression: dc.l 0 * BI_RGB biSizeImage: dc.l 0 * イメージの全バイト数 biXPelsPerMeter:dc.l 0 * 水平解像度 biYPelsPerMeter:dc.l 0 * 垂直解像度 biClrUsed: dc.w 0, 0 * 使用されている色の数 biClrImportant: dc.l 0 * 0 bmp_header_end: * BMPファイルのヘッダー領域の終わり .bss * '.bss'以降データバッファ BMPal ds.b 1024 * パレットデータバッファ(4*256byte確保する) bmp: ds.b 65536 * ピクセルデータバッファ(256*256byte確保する) .text * '.text'以降のプログラムはテキストセクションに配置される start: * スタートアドレス * ユーザーモードからスーパーバイザーモードに移行する moveq.l #_B_SUPER,d0 * d0にIOCS(BIOS)機能番号を代入 move.l #0,d1 * 数値0をd1に代入 movea.l d1,a1 * d1をa1にコピー trap #15 * IOCSコール実行 * 画面モードの設定 moveq.l #_CRTMOD,d0 * 画面モードの設定 move.w #8,d1 * 画面モードの番号 : 16 = 768x512x4 / 8 = 512x512x8 trap #15 * 画面をクリアして表示をオンにする moveq.l #_G_CLR_ON,d0 * 画面をクリアして表示をオンにする trap #15 * CG画面スクロール座標 movea.l #cg_scroll_x,a0 move.w #0,(a0)+ * X座標 move.w #0,(a0)+ * Y座標 pea msg01 * 文字列データmsg01のアドレスをスタックに積む dc.w _PRINT * 文字列の表示 addq.l #4,sp * spの位置を元に戻す pea file00 * ファイル名の先頭アドレスをスタックに積む dc.w _PRINT addq.l #4,sp pea msg_return * 改行するために改行コードのアドレスをスタックに積む dc.w _PRINT addq.l #4,sp bsr load_bmp * BMPファイル読み込みルーチンの呼び出し bsr set_palette * CGパレット設定ルーチンの呼び出し loop: * BMP画像の描画 move.w #128,d0 * BMP画像X座標( 0 - 256 ) move.w #128,d1 * BMP画像Y座標( 0 - 256 ) bsr draw_bmp * 画面描画ルーチンの呼び出し moveq.l #_BITSNS,d0 moveq.l #0,d1 trap #15 and.b #%00000010,d0 tst.b d0 bne end bra loop end: moveq.l #_CRTMOD,d0 * 画面モードの設定 move.w #16,d1 * 画面モードの番号 : 16 = 768x512x4 / 8 = 512x512x8 trap #15 * スーパーバイザーモードからユーザーモードに戻る moveq.l #_B_SUPER,d0 move.l SP,a1 trap #15 * アプリ終了 dc.w _EXIT * OSコール実行 : _EXIT = プログラムの終了 load_bmp: * BMPファイルのデータを読み込むルーチン * File Open (ファイルアクセス開始) move.w #0,-(sp) * アクセスモードを指定。0 = 読み込みモード/1 = 書き込みモード pea file00 * ファイル名が書かれた部分の先頭アドレスをスタックに積む dc.w _OPEN * OSコールの実行 : ファイルのオープン addq.l #6,sp * SPの位置を元に戻す tst.l d0 * もしd0がプラスなら、 bpl load_bmp_0 * ラベルload_bmp_0に飛ぶ。 pea msg02 * エラーメッセージ dc.w _PRINT addq.l #4,sp dc.w _EXIT * OSコール実行 : エラーによりソフトウェアの終了 load_bmp_0: move.w d0,fh * _OPENを実行した後にd0にファイルハンドルが入っているので * d0のファイル管理番号をfhに保存する * BMFH read move.l #14,-(sp) * 読み込みバイト数をスタックに積む pea BMFH * アドレスBMFHをスタックに積む move.w fh,-(sp) * ファイルハンドルをスタックに積む dc.w _READ * OSコール実行 : ファイルの読み込み add.l #10,sp * SPの位置を元に戻す cmp.w #'BM',bmph * BMPファイルヘッダーが'BM'か比較する beq load_bmp_1 * 'BM'ならload_bmp_1に飛ぶ pea msg03 dc.w _PRINT addq.l #4,sp dc.w _EXIT * 'BM'以外なのでアプリ終了 load_bmp_1: * BMIH read move.l #40,-(sp) * 40byteのファイルヘッダー pea BMIH move.w fh,-(sp) dc.w _READ * ファイル読み込みのOSコールを実行する lea 10(sp),sp move.w biBitCount,d0 ror.w #8,d0 * d0.wの中身の2進数データを右に8ビットローテート(Intel対策) cmp.w #8,d0 * 8Bitカラー(256色)か比較する beq load_bmp_2 pea msg04 dc.w _PRINT addq.l #4,sp dc.w _EXIT load_bmp_2: * 画像の横ピクセル数チェック move.w biWidth,d0 ror.w #8,d0 * Intel対策(2バイトのデータが左右逆なのでローテートで元に戻す) ext.l d0 * 16ビットのデータをプラスマイナス保ったまま32ビットに拡張する cmp.w #256,d0 * d0.wと数値768を比較する beq load_bmp_3 * branch equal / 比較結果が同じならアドレス'load_bmp_3'に飛ぶ pea msg05 dc.w _PRINT addq.l #4,sp dc.w _EXIT load_bmp_3: move.l d0,viewW * 画像の縦ピクセル数チェック move.w biHeight,d1 ror.w #8,d1 * Intel対策のためバイトデータ逆転させる ext.l d1 cmp.w #256,d1 beq load_bmp_4 pea msg05 dc.w _PRINT addq.l #4,sp dc.w _EXIT load_bmp_4: move.l d1,viewH * BMP パレットの読み込み move.l #1024,-(sp) * パレットデータのサイズをスタックに積む pea BMPal move.w fh,-(sp) dc.w _READ lea 10(sp),sp * BMP ピクセルデータの読み込み move.l #65536,-(sp) * ピクセルデータの全てのサイズをスタックに積む pea bmp * bmp = 読み込んだピクセルデータを配置するアドレス move.w fh,-(sp) dc.w _READ lea 10(sp),sp * File Close (ファイルアクセス終了) move.w fh,-(sp) dc.w _CLOSE addq.l #2,sp rts * サブルーチンの最後に配置(呼出元にリターンし次の命令から実行再開) * RGBQUAD { Blue8, Green8, Red8, Reserved8 } = %RRRRRRRR_GGGGGGGG_BBBBBBBB_AAAAAAAA * palette { Green5, Red5, Blue5, Alpha1 } = %GGGGGRRR_RRBBBBB0 * Set CG Palette set_palette: movea.l #cg_palette,a0 * CG用パレットレジスターアドレス lea BMPal,a1 * パレットデータバッファアドレス moveq.l #0,d7 set_palette_1: move.b (a1),d0 * a1が示すアドレスからd0にバイトデータを読み込む * ↓32ビット色の青色成分8ビットのうち上位5ビットを抜き取り16ビット色の青色成分として使う lsr.w #2,d0 * 青色ビットデータの位置を16ビット色内の青色位置に右シフト and.w #%0000000000111110,d0 * 16ビット色の青色成分以外のビットを0にする move.b 1(a1),d1 * a1に1を足したアドレスからd1にバイトデータを読み込む * ↓32ビット色の緑色成分8ビットのうち上位5ビットを抜き取り16ビット色の緑色成分として使う lsl.w #8,d1 * 緑色ビットデータの位置を16ビット色内の緑色位置に左シフト and.w #%1111100000000000,d1 * 16ビット色の緑色成分以外のビットを0にする move.b 2(a1),d2 * a1に2を足したアドレスからd2にバイトデータを読み込む * ↓32ビット色の赤色成分8ビットのうち上位5ビットを抜き取り16ビット色の赤色成分として使う lsl.w #3,d2 * 赤色ビットデータの位置を16ビット色内の赤色位置に左シフト and.w #%0000011111000000,d2 * 16ビット色の赤色成分以外のビットを0にする * 青赤緑のそれぞれの色成分を論理和命令で一つの16ビットカラーに完成させる or.w d1,d0 or.w d2,d0 * 完成した色データをパレットメモリーに書き込んで更新する move.w d0,(a0)+ * 16ビットデータを書き込んだ後にa0に2を足す * 次の変換元色データを取り出す前にa1のアドレスを更新しておく addq.l #4,a1 addq.w #1,d7 * 色番号を次の番号に更新する cmp.w #256,d7 * 現在の色番号と16を比較する blt set_palette_1 * 現在の色番号が16未満ならアドレスset_palette_1に飛ぶ rts * 呼出元に戻る * Draw BMP draw_bmp: * in d0.w = X座標 * in d1.w = Y座標 movem.l d0/d1/d5-d7/a1/a5/a6,-(sp) move.l #$C00000,a6 * CG-Vram先頭アドレスをa6に代入する ext.l d1 * d1をロングに拡張 moveq #10,d5 * 次の命令でd5を使うため10を入れて準備する lsl.l d5,d1 * d1の値を左に10ビットシフトして1024倍する ext.l d0 * d0をロングに拡張 add.l d0,d0 * 1ピクセルあたり2バイト(16ビット)なのでd0を2倍する add.l d1,d0 * d0にd1を足す add.l d0,a6 * a6にd0を足して描画先頭アドレスの完成 lea bmp,a1 * a1にピクセルデータの先頭アドレスを代入する move.l #256-1,d7 * d7にループ回数から1を引いた値を代入する draw_bmp_1: move.l #256-1,d6 * d6にループ回数から1を引いた値を代入する movea.l a6,a5 * a6のアドレス値をa5にコピーする draw_bmp_2: move.b (a1)+,d5 * アドレスa1のピクセルデータd5に読み込む move.w d5,(a5)+ * アドレスa5内のVRAMアドレスにd5のピクセルを置く dblt d6,draw_bmp_2 * d6から1を引いた結果0以上の時にdraw_bmp_2に飛ぶ add.l #512*2,a6 * 次のY座標を処理するためにa6に横VRAMサイズ1024を足す dblt d7,draw_bmp_1 * d7から1を引いた結果0以上の時にdraw_bmp_1に飛ぶ movem.l (sp)+,d0/d1/d5-d7/a1/a5/a6 rts * 呼出元に戻る ------------------------------------------------------------------------------------------------ コマンドプロンプトから、 A>as BMP256_1.s [Enter] A>lk BMP256_1.o [Enter] A>BMP256_1.x [Enter] のように入力すると 用意した画像ファイルが表示されます。 ソースコード中の画像の座標を書き換えて 表示する位置を変える事もできます。 ESCキーで終了します。 [ プログラムの解説 ] 今回のプログラムは第014回のプログラム内の'draw_bitmap'ルーチンを高速化したものです。 第014回の'draw_bitmap'はプログラムの読みやすさを重視した設計で 変更もやりやすいという利点があります。 ただし処理速度は犠牲になります。 今回の'draw_bitmap'は僕の能力の範囲で高速化しました。 以前は3つの命令でループしていた部分を dblt命令1つに変更する事で無駄な処理を省いて高速化しました。 応用編の一発目、いかがでしたか? 今後の予定は未定ですが、アセンブラ講座は続けたいと思っています(^^) それではまた!! [EOF]